// dingtalk/dingtalk_stream_notifier.go
package dingtalk

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"v2/config"
	"v2/model"
	"v2/util"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	dingtalkcard_1_0 "github.com/alibabacloud-go/dingtalk/card_1_0"
	dingtalkoauth2_1_0 "github.com/alibabacloud-go/dingtalk/oauth2_1_0"
	teautils "github.com/alibabacloud-go/tea-utils/v2/service"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/google/uuid"
	"github.com/open-dingtalk/dingtalk-stream-sdk-go/card"
	"github.com/open-dingtalk/dingtalk-stream-sdk-go/chatbot"
	"github.com/open-dingtalk/dingtalk-stream-sdk-go/client"
	"github.com/open-dingtalk/dingtalk-stream-sdk-go/logger"
)

// DingTalkStreamNotifier 钉钉Stream通知器
type DingTalkStreamNotifier struct {
	ClientID            string
	ClientSecret        string
	CardTemplateID      string
	GroupConversationID string
}

// DingTalkUserInfo 钉钉用户信息结构体
type DingTalkUserInfo struct {
	UserID   string `json:"user_id"`   // 用户ID
	Name     string `json:"name"`      // 姓名
	Avatar   string `json:"avatar"`    // 头像
	Mobile   string `json:"mobile"`    // 手机号
	Email    string `json:"email"`     // 邮箱
	JobTitle string `json:"job_title"` // 职位
}

// NewDingTalkStreamNotifier 创建钉钉Stream通知器
func NewDingTalkStreamNotifier() *DingTalkStreamNotifier {
	return &DingTalkStreamNotifier{
		ClientID:            config.Global.Notifier.DingTalkStream.ClientID,
		ClientSecret:        config.Global.Notifier.DingTalkStream.ClientSecret,
		CardTemplateID:      config.Global.Notifier.DingTalkStream.CardTemplateID,
		GroupConversationID: config.Global.Notifier.DingTalkStream.GroupConversationID,
	}
}

// GetAccessToken 获取钉钉访问令牌
func (c *DingTalkStreamNotifier) GetAccessToken() (string, error) {
	// 创建OpenAPI配置
	config := &openapi.Config{
		Protocol: tea.String("https"),
		RegionId: tea.String("central"),
	}

	// 创建OAuth2.0客户端
	oauthClient, err := dingtalkoauth2_1_0.NewClient(config)
	if err != nil {
		return "", fmt.Errorf("创建OAuth2.0客户端失败: %v", err)
	}

	// 构建请求
	request := &dingtalkoauth2_1_0.GetAccessTokenRequest{
		AppKey:    tea.String(c.ClientID),
		AppSecret: tea.String(c.ClientSecret),
	}

	// 发送请求
	response, err := oauthClient.GetAccessToken(request)
	if err != nil {
		return "", fmt.Errorf("获取访问令牌失败: %v", err)
	}

	// 检查响应
	if response == nil || response.Body == nil || response.Body.AccessToken == nil {
		return "", fmt.Errorf("获取访问令牌失败: 响应格式不正确")
	}
	log.Printf("[DingTalk] 获取访问令牌成功: %s", *response.Body.AccessToken)
	return *response.Body.AccessToken, nil
}

// GetUserInfo 根据用户ID获取用户信息（使用topapi/v2/user/get）
func (c *DingTalkStreamNotifier) GetUserInfo(userID string) (*DingTalkUserInfo, error) {
	if userID == "" {
		return nil, fmt.Errorf("用户ID不能为空")
	}

	// 获取访问令牌
	accessToken, err := c.GetAccessToken()
	if err != nil {
		return nil, fmt.Errorf("获取访问令牌失败: %v", err)
	}

	// 构建请求URL（使用topapi v2 API）
	url := "https://oapi.dingtalk.com/topapi/v2/user/get"

	// 构建表单数据（按照官方文档格式）
	data := fmt.Sprintf("access_token=%s&userid=%s", accessToken, userID)
	log.Printf("[DingTalk] 请求URL: %s", url)
	log.Printf("[DingTalk] 请求数据: %s", data)

	// 发送HTTP POST请求
	resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data))
	if err != nil {
		return nil, fmt.Errorf("HTTP请求失败: %v", err)
	}
	defer resp.Body.Close()

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应体失败: %v", err)
	}

	log.Printf("[DingTalk] API响应: %s", string(body))

	// 解析响应JSON
	var response struct {
		Errcode int    `json:"errcode"`
		Errmsg  string `json:"errmsg"`
		Result  struct {
			Userid string `json:"userid"`
			Name   string `json:"name"`
			Avatar string `json:"avatar"`
			Mobile string `json:"mobile"`
			Email  string `json:"email"`
			Active bool   `json:"active"`
			Admin  bool   `json:"admin"`
			Title  string `json:"title"`
		} `json:"result"`
	}

	if err := json.Unmarshal(body, &response); err != nil {
		return nil, fmt.Errorf("解析响应JSON失败: %v", err)
	}

	// 检查API调用是否成功
	if response.Errcode != 0 {
		return nil, fmt.Errorf("钉钉API调用失败: errcode=%d, errmsg=%s", response.Errcode, response.Errmsg)
	}

	// 构建用户信息结构体
	userInfo := &DingTalkUserInfo{
		UserID:   response.Result.Userid,
		Name:     response.Result.Name,
		Avatar:   response.Result.Avatar,
		Mobile:   response.Result.Mobile,
		Email:    response.Result.Email,
		JobTitle: response.Result.Title,
	}

	log.Printf("[DingTalk] 获取用户信息成功: ID=%s, 姓名=%s, 状态=%t", userInfo.UserID, userInfo.Name, response.Result.Active)
	return userInfo, nil
}

// SendAlertCard 发送告警卡片
func (c *DingTalkStreamNotifier) SendAlertCard(alert model.AlertInfo) error {
	if !config.Global.Notifier.DingTalkStream.Enable {
		log.Println("[DingTalk] 钉钉Stream功能未启用")
		return nil
	}

	log.Printf("[DingTalk] 开始发送告警卡片，告警ID: %s, 规则名: %s", alert.ID, alert.RuleName)

	// 获取访问令牌
	accessToken, err := c.GetAccessToken()
	if err != nil {
		log.Printf("[DingTalk] 获取访问令牌失败: %v", err)
		return err
	}

	// 构建卡片数据
	cardData := &dingtalkcard_1_0.CreateAndDeliverRequestCardData{
		CardParamMap: make(map[string]*string),
	}

	// 填充卡片参数
	cardData.CardParamMap["title"] = tea.String(fmt.Sprintf("【告警】%s", alert.RuleName))
	cardData.CardParamMap["rule_name"] = tea.String(alert.RuleName)
	cardData.CardParamMap["alert_level"] = tea.String(alert.SeverityText)
	cardData.CardParamMap["formatted_time"] = tea.String(alert.FormattedTime)
	cardData.CardParamMap["trigger_value"] = tea.String(alert.TriggerValue)
	cardData.CardParamMap["group_name"] = tea.String(alert.GroupName)
	cardData.CardParamMap["tags"] = tea.String(c.formatTags(alert.TagsMap))
	cardData.CardParamMap["alert_id"] = tea.String(alert.ID)
	cardData.CardParamMap["status"] = tea.String("active")

	// 生成唯一跟踪ID
	u, _ := uuid.NewUUID()
	outTrackId := u.String()

	// 建立OutTrackId和AlertID的映射关系（存储到Redis）
	trackingKey := fmt.Sprintf("dingtalk_track:%s", outTrackId)
	if err := util.SaveAlertVariables(trackingKey, map[string]interface{}{
		"alert_id":   alert.ID,
		"created_at": time.Now().Unix(),
	}); err != nil {
		log.Printf("[DingTalk] 保存追踪映射失败: %v", err)
	}

	// 构建发送卡片请求
	sendCardRequest := &dingtalkcard_1_0.CreateAndDeliverRequest{
		UserIdType:     tea.Int32(1), // 1（默认）：userid模式；2：unionId模式;
		CardTemplateId: tea.String(c.CardTemplateID),
		OutTrackId:     tea.String(outTrackId),
		CallbackType:   tea.String("STREAM"), // 采用 Stream 模式接收回调事件
		CardData:       cardData,
	}

	// 根据是否配置群ID确定发送目标
	if c.GroupConversationID != "" {
		// 修复群ID格式：确保以"cid"开头
		groupID := c.GroupConversationID
		if !strings.HasPrefix(groupID, "cid") {
			log.Printf("[DingTalk] 群ID格式修正: %s -> cid%s", groupID, groupID)
			groupID = "cid" + groupID
		}

		log.Printf("[DingTalk] 发送卡片到群聊，群ID: %s", groupID)

		sendCardRequest.OpenSpaceId = tea.String(fmt.Sprintf("dtv1.card//IM_GROUP.%s", groupID))

		// 设置群聊空间模型
		sendCardRequest.ImGroupOpenSpaceModel = &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenSpaceModel{
			SupportForward: tea.Bool(true),
		}

		sendCardRequest.ImGroupOpenDeliverModel = &dingtalkcard_1_0.CreateAndDeliverRequestImGroupOpenDeliverModel{
			Extension: map[string]*string{
				"custom_card_type": tea.String("single"),
			},
			RobotCode: tea.String(c.ClientID),
		}
	} else {
		log.Printf("[DingTalk] 发送卡片到个人")

		// 这里可以修改为发送给特定用户，或使用配置中的用户ID
		sendCardRequest.OpenSpaceId = tea.String("dtv1.card//IM_ROBOT.all")

		// 设置个人空间模型
		sendCardRequest.ImRobotOpenSpaceModel = &dingtalkcard_1_0.CreateAndDeliverRequestImRobotOpenSpaceModel{
			SupportForward: tea.Bool(true),
		}

		sendCardRequest.ImRobotOpenDeliverModel = &dingtalkcard_1_0.CreateAndDeliverRequestImRobotOpenDeliverModel{
			Extension: map[string]*string{
				"custom_card_type": tea.String("single"),
			},
			RobotCode: tea.String(c.ClientID),
			SpaceType: tea.String("IM_ROBOT"),
		}
	}

	// 创建OpenAPI配置
	apiConfig := &openapi.Config{
		Protocol: tea.String("https"),
		RegionId: tea.String("central"),
	}

	// 创建卡片客户端
	cardClient, err := dingtalkcard_1_0.NewClient(apiConfig)
	if err != nil {
		return fmt.Errorf("创建卡片客户端失败: %v", err)
	}

	// 设置请求头
	headers := &dingtalkcard_1_0.CreateAndDeliverHeaders{
		XAcsDingtalkAccessToken: tea.String(accessToken),
	}

	// 发送卡片
	resp, err := cardClient.CreateAndDeliverWithOptions(sendCardRequest, headers, &teautils.RuntimeOptions{})
	if err != nil {
		// 详细记录SDK错误
		var sdkError = &tea.SDKError{}
		if _t, ok := err.(*tea.SDKError); ok {
			sdkError = _t
		} else {
			sdkError.Message = tea.String(err.Error())
		}

		if !tea.BoolValue(teautils.Empty(sdkError.Code)) && !tea.BoolValue(teautils.Empty(sdkError.Message)) {
			log.Printf("[DingTalk] 发送卡片失败, clientId=%s, err=%+v", c.ClientID, sdkError)
		}

		return fmt.Errorf("发送卡片失败: %v", err)
	}

	// 检查响应
	if resp == nil || resp.Body == nil {
		return fmt.Errorf("发送卡片失败: 响应为空")
	}

	// 检查发送结果
	if resp.Body.Result != nil && resp.Body.Result.DeliverResults != nil {
		for _, result := range resp.Body.Result.DeliverResults {
			if !*result.Success {
				log.Printf("[DingTalk] 卡片发送失败: spaceId=%s, errorMsg=%s", *result.SpaceId, *result.ErrorMsg)
				return fmt.Errorf("卡片发送失败: %s", *result.ErrorMsg)
			}
		}
	}

	log.Printf("[DingTalk] 成功发送告警卡片: %s", alert.RuleName)

	// 保存告警变量到 Redis，供后续回调使用
	variables := map[string]interface{}{
		"alert_id":       alert.ID,
		"rule_name":      alert.RuleName,
		"alert_level":    alert.SeverityText,
		"formatted_time": alert.FormattedTime,
		"trigger_value":  alert.TriggerValue,
		"group_name":     alert.GroupName,
		"tags":           c.formatTags(alert.TagsMap),
		"status":         "active",
		"severity":       alert.Severity,
		"datasource_id":  alert.DatasourceID,
		"group_id":       alert.GroupID,
	}

	// 将TagsMap转换为Redis可存储的格式
	for k, v := range alert.TagsMap {
		variables["tag_"+k] = v
	}

	log.Printf("[DingTalk] 尝试保存告警变量到 Redis，告警ID: %s", alert.ID)
	if err := util.SaveAlertVariables(alert.ID, variables); err != nil {
		log.Printf("[DingTalk] 保存告警变量到 Redis 失败: %v", err)
		// 不返回错误，因为即使保存失败，卡片仍应发送
	} else {
		log.Printf("[DingTalk] 成功保存告警变量到 Redis，告警ID: %s", alert.ID)
	}

	return nil
}

// handleCardCallback 处理卡片回调事件（占位器）
func (h *DingTalkStreamHandler) handleCardCallback(action, alertID, userID, notes string) error {
	log.Printf("[DingTalk] 处理卡片回调: action=%s, alertID=%s, userID=%s, notes=%s", action, alertID, userID, notes)

	// 根据操作类型调用相应的处理方法
	switch action {
	case "claim":
		return h.StreamNotifier.HandleClaimAlert(alertID, userID, notes)
	case "silence2":
		return h.StreamNotifier.HandleMuteAlert(alertID, userID, notes, config.Global.Nightingale.MuteDuration2h, "屏蔽2小时")
	case "silence24":
		return h.StreamNotifier.HandleMuteAlert(alertID, userID, notes, config.Global.Nightingale.MuteDuration24h, "屏蔽24小时")
	case "complete_alarm":
		return h.StreamNotifier.HandleCompleteAlert(alertID, userID, notes)
	default:
		return fmt.Errorf("未知的操作类型: %s", action)
	}
}

// formatTags 格式化标签信息
func (c *DingTalkStreamNotifier) formatTags(tagsMap map[string]string) string {
	if len(tagsMap) == 0 {
		return "无标签"
	}

	var result string
	for k, v := range tagsMap {
		result += fmt.Sprintf("- `%s`: `%s`\n", k, v)
	}
	return result
}

// 钉钉Stream处理器类型
type DingTalkStreamHandler struct {
	StreamNotifier *DingTalkStreamNotifier
	StreamClient   *client.StreamClient
	isRunning      bool
	mu             sync.Mutex
}

var (
	// 钉钉Stream处理器单例
	dingTalkStreamInstance *DingTalkStreamHandler
	dingTalkStreamOnce     sync.Once
	// 全局钉钉客户端实例（用于回调处理）
	dingtalkClient *DingTalkStreamNotifier
)

// GetDingTalkStreamHandler 获取钉钉Stream处理器单例
func GetDingTalkStreamHandler() *DingTalkStreamHandler {
	dingTalkStreamOnce.Do(func() {
		// 从配置中获取钉钉应用配置
		dingTalkConfig := config.Global.Notifier.DingTalkStream
		log.Printf("[DingTalk] 加载配置: %+v", dingTalkConfig)

		// 设置日志记录器（按照官方示例）
		logger.SetLogger(logger.NewStdTestLogger())

		// 创建 Stream 通知器
		streamNotifier := NewDingTalkStreamNotifier()
		dingtalkClient = streamNotifier // 设置全局实例

		// 创建 Stream 客户端（按照官方示例）
		streamClient := client.NewStreamClient(client.WithAppCredential(client.NewAppCredentialConfig(dingTalkConfig.ClientID, dingTalkConfig.ClientSecret)))

		// 创建处理器实例
		handler := &DingTalkStreamHandler{
			StreamNotifier: streamNotifier,
			StreamClient:   streamClient,
			isRunning:      false,
		}

		// 按照官方示例注册回调处理器
		streamClient.RegisterChatBotCallbackRouter(OnChatBotMessageReceived)
		streamClient.RegisterCardCallbackRouter(OnCardCallback)

		dingTalkStreamInstance = handler

		// 如果钉钉应用已启用，自动启动 Stream 连接
		if dingTalkConfig.Enable {
			go dingTalkStreamInstance.Start()
		}
	})

	return dingTalkStreamInstance
}

// Start 启动钉钉 Stream 连接（按照官方示例方式）
func (h *DingTalkStreamHandler) Start() {
	h.mu.Lock()
	defer h.mu.Unlock()

	if h.isRunning {
		log.Println("[DingTalk] Stream 连接已在运行中")
		return
	}

	// 从配置中获取钉钉应用配置
	dingTalkConfig := config.Global.Notifier.DingTalkStream

	// 检查是否启用钉钉应用
	if !dingTalkConfig.Enable {
		log.Println("[DingTalk] 钉钉应用未启用，跳过 Stream 连接")
		return
	}

	// 检查必要配置
	if dingTalkConfig.ClientID == "" || dingTalkConfig.ClientSecret == "" {
		log.Println("[DingTalk] 钉钉应用配置不完整，缺少 ClientID 或 ClientSecret，跳过 Stream 连接")
		return
	}

	// 标记为运行中
	h.isRunning = true

	// 按照官方示例启动Stream连接（阻塞模式）
	log.Println("[DingTalk] 开始启动 Stream 连接...")
	err := h.StreamClient.Start(context.Background())
	if err != nil {
		log.Printf("[DingTalk] Stream 连接失败: %v", err)
		h.isRunning = false
	} else {
		log.Println("[DingTalk] Stream 连接已成功建立")
	}
}

// Stop 停止钉钉 Stream 连接
func (h *DingTalkStreamHandler) Stop() {
	h.mu.Lock()
	defer h.mu.Unlock()

	if !h.isRunning {
		log.Println("[DingTalk] Stream 连接未运行")
		return
	}

	h.isRunning = false
	log.Println("[DingTalk] Stream 连接已停止")
}

// IsRunning 检查 Stream 连接是否运行中
func (h *DingTalkStreamHandler) IsRunning() bool {
	h.mu.Lock()
	defer h.mu.Unlock()
	return h.isRunning
}

// SendAlert 发送告警卡片
func (h *DingTalkStreamHandler) SendAlert(alert model.AlertInfo) error {
	if h.StreamNotifier == nil {
		return fmt.Errorf("Stream 通知器未初始化")
	}

	// 发送告警卡片
	err := h.StreamNotifier.SendAlertCard(alert)
	if err != nil {
		return err
	}

	return nil
}

// OnCardCallback 卡片交互回调处理（按照官方示例格式）
func OnCardCallback(ctx context.Context, request *card.CardRequest) (*card.CardResponse, error) {
	logger.GetLogger().Infof("receive card data: %v", request) // 按照官方示例的日志格式

	// 输出完整的回调参数详情，便于调试和卡片模板调整
	log.Printf("[DingTalk] ===================钉钉回调参数详情===================")
	log.Printf("[DingTalk] OutTrackId: %s", request.OutTrackId)
	log.Printf("[DingTalk] CorpId: %s", request.CorpId)
	log.Printf("[DingTalk] UserId: %s", request.UserId)
	log.Printf("[DingTalk] UserIdType: %d", request.UserIdType)
	log.Printf("[DingTalk] SpaceId: %s", request.SpaceId)
	log.Printf("[DingTalk] SpaceType: %s", request.SpaceType)
	log.Printf("[DingTalk] Type: %s", request.Type)
	log.Printf("[DingTalk] Content: %s", request.Content)
	log.Printf("[DingTalk] Extension: %s", request.Extension)

	// 输出可用的参数获取方式
	log.Printf("[DingTalk] -------可获取的参数-------")
	action := request.GetActionString("action")
	alertID := request.GetActionString("alert_id")
	userID := request.GetActionString("user_id")
	notes := request.GetActionString("notes")
	timeStr := request.GetActionString("time")
	status := request.GetActionString("status")
	operator := request.GetActionString("operator")
	reason := request.GetActionString("reason")

	// 如果从params中获取不到user_id，尝试从 CardRequest 的 UserId 字段获取
	if userID == "" && request.UserId != "" {
		userID = request.UserId
		log.Printf("[DingTalk] 从CardRequest.UserId获取到用户ID: %s", userID)
	}

	// 获取用户信息（如果有用户ID的话）
	var userName string
	if userID != "" && dingtalkClient != nil {
		if userInfo, err := dingtalkClient.GetUserInfo(userID); err == nil {
			userName = userInfo.Name
			log.Printf("[DingTalk] 获取到用户信息: %s (%s)", userName, userID)
		} else {
			log.Printf("[DingTalk] 获取用户信息失败: %v", err)
			userName = userID // fallback 到用户ID
		}
	} else {
		userName = userID
	}

	log.Printf("[DingTalk] action: %s", action)
	log.Printf("[DingTalk] alert_id: %s", alertID)
	log.Printf("[DingTalk] user_id: %s", userID)
	log.Printf("[DingTalk] notes: %s", notes)
	log.Printf("[DingTalk] time: %s", timeStr)
	log.Printf("[DingTalk] status: %s", status)
	log.Printf("[DingTalk] operator: %s", operator)
	log.Printf("[DingTalk] reason: %s", reason)
	log.Printf("[DingTalk] ============================================================")

	// 如果通过常规方式获取不到alert_id，尝试通过OutTrackId查找
	if alertID == "" && request.OutTrackId != "" {
		// 尝试从OutTrackId查找对应的告警ID
		trackingKey := fmt.Sprintf("dingtalk_track:%s", request.OutTrackId)
		trackingVars, exists, err := util.GetAlertVariables(trackingKey)
		if err != nil {
			log.Printf("[DingTalk] 查找追踪映射失败: %v", err)
		} else if exists {
			if trackAlertID, ok := trackingVars["alert_id"].(string); ok {
				alertID = trackAlertID
				log.Printf("[DingTalk] 通过OutTrackId %s 找到告警ID: %s", request.OutTrackId, alertID)
			}
		}
	}

	log.Printf("[DingTalk] 处理操作: %s, 告警ID: %s, 用户: %s(%s), 备注: %s", action, alertID, userName, userID, notes)

	// 如果没有alert_id，先记录错误但继续尝试处理
	if alertID == "" {
		log.Printf("[DingTalk] 警告：缺少告警ID，无法处理此操作，请检查卡片模板配置")

		// 返回错误响应，提示用户
		response := &card.CardResponse{
			CardData: &card.CardDataDto{
				CardParamMap: map[string]string{
					"status": "错误：缺少告警ID",
				},
			},
		}
		return response, fmt.Errorf("缺少告警ID，请检查卡片模板配置")
	}

	// 根据操作类型处理
	var err error
	if dingtalkClient != nil {
		switch action {
		case "claim":
			err = dingtalkClient.HandleClaimAlert(alertID, userID, notes)
		case "silence2":
			err = dingtalkClient.HandleMuteAlert(alertID, userID, notes,
				config.Global.Nightingale.MuteDuration2h, "屏蔽2小时")
		case "silence24":
			err = dingtalkClient.HandleMuteAlert(alertID, userID, notes,
				config.Global.Nightingale.MuteDuration24h, "屏蔽24小时")
		case "complete_alarm":
			err = dingtalkClient.HandleCompleteAlert(alertID, userID, notes)
		default:
			log.Printf("[DingTalk] 未知的操作类型: %s", action)
		}
	}

	if err != nil {
		log.Printf("[DingTalk] 处理操作失败: %v", err)
		return nil, err
	}

	// 从Redis获取原始告警信息以构建更新后的卡片
	var updatedCardData map[string]string
	var operationNotes string

	if alertID != "" {
		// 获取更新后的告警变量
		updatedVariables, exists, err := util.GetAlertVariables(alertID)
		if err == nil && exists {
			// 根据操作类型生成操作记录文本（包含时间）
			currentTime := time.Now().Format("2006-01-02 15:04:05")
			switch action {
			case "claim":
				operationNotes = fmt.Sprintf("用户%s 进行告警认领 [%s]", userName, currentTime)
			case "silence2":
				operationNotes = fmt.Sprintf("用户%s 屏蔽2小时告警 [%s]", userName, currentTime)
			case "silence24":
				operationNotes = fmt.Sprintf("用户%s 屏蔽24小时告警 [%s]", userName, currentTime)
			case "complete_alarm":
				operationNotes = fmt.Sprintf("用户%s 进行告警完成 [%s]", userName, currentTime)
			default:
				operationNotes = fmt.Sprintf("用户%s 执行了操作: %s [%s]", userName, action, currentTime)
			}

			// 构建更新后的卡片参数（保留所有原始信息）
			updatedCardData = map[string]string{
				"title":          fmt.Sprintf("【告警】%s", getStringFromMap(updatedVariables, "rule_name", "告警")),
				"rule_name":      getStringFromMap(updatedVariables, "rule_name", ""),
				"alert_level":    getStringFromMap(updatedVariables, "alert_level", ""),
				"formatted_time": getStringFromMap(updatedVariables, "formatted_time", ""),
				"trigger_value":  getStringFromMap(updatedVariables, "trigger_value", ""),
				"group_name":     getStringFromMap(updatedVariables, "group_name", ""),
				"tags":           getStringFromMap(updatedVariables, "tags", ""),
				"alert_id":       alertID,
				"status":         getStringFromMap(updatedVariables, "status", "processed"),
				"notes":          operationNotes, // 关键：在notes字段显示操作信息
			}

			log.Printf("[DingTalk] 生成操作记录文本: %s", operationNotes)
		} else {
			log.Printf("[DingTalk] 无法获取更新后的告警变量，使用默认操作信息")
			// 即使获取变量失败，也要生成操作记录（包含时间）
			currentTime := time.Now().Format("2006-01-02 15:04:05")
			switch action {
			case "claim":
				operationNotes = fmt.Sprintf("用户%s 进行告警认领 [%s]", userName, currentTime)
			case "silence2":
				operationNotes = fmt.Sprintf("用户%s 屏蔽2小时告警 [%s]", userName, currentTime)
			case "silence24":
				operationNotes = fmt.Sprintf("用户%s 屏蔽24小时告警 [%s]", userName, currentTime)
			case "complete_alarm":
				operationNotes = fmt.Sprintf("用户%s 进行告警完成 [%s]", userName, currentTime)
			default:
				operationNotes = fmt.Sprintf("用户%s 执行了操作: %s [%s]", userName, action, currentTime)
			}

			updatedCardData = map[string]string{
				"status": "已处理",
				"notes":  operationNotes,
			}
		}
	} else {
		// 没有alertID的情况下，仍然生成操作记录（包含时间）
		currentTime := time.Now().Format("2006-01-02 15:04:05")
		switch action {
		case "claim":
			operationNotes = fmt.Sprintf("用户%s 进行告警认领 [%s]", userName, currentTime)
		case "silence2":
			operationNotes = fmt.Sprintf("用户%s 屏蔽2小时告警 [%s]", userName, currentTime)
		case "silence24":
			operationNotes = fmt.Sprintf("用户%s 屏蔽24小时告警 [%s]", userName, currentTime)
		case "complete_alarm":
			operationNotes = fmt.Sprintf("用户%s 进行告警完成 [%s]", userName, currentTime)
		default:
			operationNotes = fmt.Sprintf("用户%s 执行了操作: %s [%s]", userName, action, currentTime)
		}

		updatedCardData = map[string]string{
			"status": "已处理",
			"notes":  operationNotes,
		}
	}

	// 构建响应数据（按照官方示例格式，返回完整的卡片数据）
	// 注意：CardParamMap 的值类型是 map[string]string，不是 map[string]*string
	response := &card.CardResponse{
		CardData: &card.CardDataDto{
			CardParamMap: updatedCardData,
		},
	}

	// 详细调试信息：输出返回的卡片数据
	log.Printf("[DingTalk] ===================返回卡片数据===================")
	for key, value := range updatedCardData {
		log.Printf("[DingTalk]   %s: %s", key, value)
	}
	log.Printf("[DingTalk] notes字段内容: '%s'", updatedCardData["notes"])
	log.Printf("[DingTalk] 操作处理成功: %s", action)
	log.Printf("[DingTalk] ================================================================")

	return response, nil
}

// getStringFromMap 从map中安全获取字符串值的辅助函数
func getStringFromMap(m map[string]interface{}, key, defaultValue string) string {
	if value, exists := m[key]; exists {
		if strValue, ok := value.(string); ok {
			return strValue
		}
	}
	return defaultValue
}

// OnChatBotMessageReceived 机器人消息回调处理（按照官方示例格式）
func OnChatBotMessageReceived(ctx context.Context, data *chatbot.BotCallbackDataModel) ([]byte, error) {
	logger.GetLogger().Infof("receive chatbot message: %v", data)
	// 这里可以处理机器人消息，比如触发发送告警卡片等
	return []byte(""), nil
}

// 辅助函数，从飞书代码复用

// extractDatasourceIDs 从变量中提取数据源ID
func extractDatasourceIDs(variables map[string]interface{}) []int {
	datasourceIDs := []int{}
	if dsID, ok := variables["datasource_id"]; ok {
		switch v := dsID.(type) {
		case int:
			datasourceIDs = []int{v}
		case float64:
			datasourceIDs = []int{int(v)}
		case []interface{}:
			for _, id := range v {
				if intID, ok := id.(int); ok {
					datasourceIDs = append(datasourceIDs, intID)
				} else if floatID, ok := id.(float64); ok {
					datasourceIDs = append(datasourceIDs, int(floatID))
				}
			}
		case string:
			idsStr := strings.Split(v, ",")
			for _, idStr := range idsStr {
				idStr = strings.TrimSpace(idStr)
				if id, err := strconv.Atoi(idStr); err == nil {
					datasourceIDs = append(datasourceIDs, id)
				}
			}
		}
	}
	return datasourceIDs
}

// extractGroupID 从变量中提取分组ID
func extractGroupID(variables map[string]interface{}) int {
	groupID := 0
	if gID, ok := variables["group_id"]; ok {
		switch v := gID.(type) {
		case int:
			groupID = v
		case float64:
			groupID = int(v)
		case string:
			if id, err := strconv.Atoi(v); err == nil {
				groupID = id
			}
		}
	}
	return groupID
}

// extractSeverities 从变量中提取严重级别
func extractSeverities(variables map[string]interface{}) []int {
	severities := []int{}
	if sev, ok := variables["severity"]; ok {
		switch v := sev.(type) {
		case int:
			severities = []int{v}
		case float64:
			severities = []int{int(v)}
		case []interface{}:
			for _, s := range v {
				if intS, ok := s.(int); ok {
					severities = append(severities, intS)
				} else if floatS, ok := s.(float64); ok {
					severities = append(severities, int(floatS))
				}
			}
		case string:
			sevStr := strings.Split(v, ",")
			for _, sStr := range sevStr {
				sStr = strings.TrimSpace(sStr)
				if s, err := strconv.Atoi(sStr); err == nil {
					severities = append(severities, s)
				}
			}
		}
	}
	if len(severities) == 0 {
		severities = []int{2, 3} // 默认严重级别
	}
	return severities
}

// extractTagsFromVariables 从变量中提取标签
func extractTagsFromVariablesDingTalk(variables map[string]interface{}) map[string]string {
	tags := make(map[string]string)

	// 从 tags变量中提取标签
	if tagsStr, ok := variables["tags"].(string); ok {
		// 假设格式是 "- `key`: `value`\n- `key2`: `value2`"
		lines := strings.Split(tagsStr, "\n")
		for _, line := range lines {
			if strings.HasPrefix(line, "- `") {
				// 移除前缀和后缀
				line = strings.TrimPrefix(line, "- `")
				line = strings.TrimSuffix(line, "`")

				// 分割key和value
				parts := strings.SplitN(line, "`: `", 2)
				if len(parts) == 2 {
					tags[parts[0]] = parts[1]
				}
			}
		}
	}

	return tags
}

// 添加处理告警操作的方法（使用同样的逻辑，但输出格式适配钉钉）

// HandleClaimAlert 处理告警认领 - 模拟飞书的功能
func (c *DingTalkStreamNotifier) HandleClaimAlert(alertID, userID, notes string) error {
	// 获取用户信息
	var userName string
	if userInfo, err := c.GetUserInfo(userID); err == nil {
		userName = userInfo.Name
	} else {
		log.Printf("[DingTalk] 获取用户信息失败: %v", err)
		userName = userID // fallback 到用户ID
	}

	log.Printf("[DingTalk] 用户 %s(%s) 认领告警 %s 备注: %s", userName, userID, alertID, notes)

	// 从 Redis 获取原始告警的所有变量
	originalVariables, exists, err := util.GetAlertVariables(alertID)
	if err != nil {
		log.Printf("[DingTalk] 从 Redis 获取原始告警变量失败: %v", err)
		return err
	} else if !exists {
		log.Printf("[DingTalk] Redis 中未找到告警 %s 的原始变量", alertID)
		return fmt.Errorf("告警信息不存在")
	}

	// 合并认领信息
	allVariables := make(map[string]interface{})
	for k, v := range originalVariables {
		allVariables[k] = v
	}

	allVariables["user_id"] = userID
	allVariables["complete_time"] = fmt.Sprintf("%d", time.Now().Unix())
	allVariables["notes"] = notes
	allVariables["status"] = "claimed"

	// 保存更新后的变量到 Redis
	if err := util.SaveAlertVariables(alertID, allVariables); err != nil {
		log.Printf("[DingTalk] 保存已认领状态失败: %v", err)
		return err
	}

	log.Printf("[DingTalk] 告警 %s 已成功认领", alertID)
	return nil
}

// HandleMuteAlert 处理告警屏蔽
func (c *DingTalkStreamNotifier) HandleMuteAlert(alertID, userID, notes string, durationHours int, muteName string) error {
	log.Printf("[DingTalk] 用户 %s 请求 %s 告警 %s", userID, muteName, alertID)

	// 从 Redis 获取原始告警的所有变量
	originalVariables, exists, err := util.GetAlertVariables(alertID)
	if err != nil {
		log.Printf("[DingTalk] 从 Redis 获取原始告警变量失败: %v", err)
		return err
	} else if !exists {
		log.Printf("[DingTalk] Redis 中未找到告警 %s 的原始变量", alertID)
		return fmt.Errorf("告警信息不存在")
	}

	// 提取数据源ID、分组ID、严重级别（复用飞书的逻辑）
	datasourceIDs := extractDatasourceIDs(originalVariables)
	groupID := extractGroupID(originalVariables)
	severities := extractSeverities(originalVariables)

	// 构建屏蔽规则
	now := time.Now()
	btime := now.Unix()
	etime := now.Add(time.Duration(durationHours) * time.Hour).Unix()

	// 提取标签
	tags := extractTagsFromVariablesDingTalk(originalVariables)
	tagConditions := make([]util.TagCondition, 0, len(tags))
	for key, value := range tags {
		tagConditions = append(tagConditions, util.TagCondition{
			Func:  "==",
			Key:   key,
			Value: value,
		})
	}

	// 创建屏蔽规则
	note := fmt.Sprintf("%s - %s", muteName)
	cause := fmt.Sprintf("%s - %s", muteName, now.Format("2006-01-02 15:04:05"))

	muteRule := util.MuteRule{
		GroupID:       groupID,
		Note:          note,
		Severities:    severities,
		DatasourceIDs: datasourceIDs,
		BTime:         int(btime),
		ETime:         int(etime),
		Tags:          tagConditions,
		Cause:         cause,
		MuteTimeType:  0,
	}

	// 调用夜莺V8 API屏蔽告警
	client := util.NewNightingaleV8Client()
	muteResp, err := client.MuteAlert(muteRule)
	if err != nil {
		log.Printf("[DingTalk] 屏蔽告警失败: %v", err)
		return err
	}

	// 构建已屏蔽状态变量
	mutedTime := now.Format("2006-01-02 15:04:05")
	endTime := time.Unix(etime, 0).Format("2006-01-02 15:04:05")

	allVariables := make(map[string]interface{})
	for k, v := range originalVariables {
		allVariables[k] = v
	}

	allVariables["muted_time"] = mutedTime
	allVariables["muted_reason"] = muteName
	allVariables["muted_duration"] = fmt.Sprintf("%d小时", durationHours)
	allVariables["muted_end_time"] = endTime
	allVariables["mute_id"] = muteResp.Data.ID
	allVariables["status"] = "muted"
	allVariables["user_id"] = userID
	allVariables["complete_time"] = fmt.Sprintf("%d", time.Now().Unix())
	allVariables["notes"] = notes

	// 保存更新后的变量到 Redis
	if err := util.SaveAlertVariables(alertID, allVariables); err != nil {
		log.Printf("[DingTalk] 保存已屏蔽状态失赅: %v", err)
		return err
	}

	log.Printf("[DingTalk] 告警 %s 已成功%s", alertID, muteName)
	return nil
}

// HandleCompleteAlert 处理告警完成
func (c *DingTalkStreamNotifier) HandleCompleteAlert(alertID, userID, notes string) error {
	log.Printf("[DingTalk] 用户 %s 处理完成告警 %s: %s", userID, alertID, notes)

	// 从 Redis 获取原始告警的所有变量
	originalVariables, exists, err := util.GetAlertVariables(alertID)
	if err != nil {
		log.Printf("[DingTalk] 从 Redis 获取原始告警变量失败: %v", err)
		return err
	} else if !exists {
		log.Printf("[DingTalk] Redis 中未找到告警 %s 的原始变量", alertID)
		return fmt.Errorf("告警信息不存在")
	}

	// 合并原始变量和新变量
	allVariables := make(map[string]interface{})
	for k, v := range originalVariables {
		allVariables[k] = v
	}

	allVariables["user_id"] = userID
	allVariables["complete_time"] = fmt.Sprintf("%d", time.Now().Unix())
	allVariables["notes"] = notes
	allVariables["status"] = "resolved"

	// 保存更新后的变量到 Redis
	if err := util.SaveAlertVariables(alertID, allVariables); err != nil {
		log.Printf("[DingTalk] 保存已完成状态失败: %v", err)
		return err
	}

	log.Printf("[DingTalk] 告警 %s 已成功处理完成", alertID)
	return nil
}
